﻿/*
 * @Autor: PK
 * @Date: 2021/12/30 14:06:SS
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Xml;
using Itinero;
using Itinero.IO.Osm;
using Itinero.LocalGeo;
using Itinero.Profiles;
using OsmSharp.Tags;

namespace ShkSimulation.core.util.osm {
	
	public class Bound {

		public static Bound FromString(string jsonString) {
			return JsonSerializer.Deserialize<Bound>(jsonString);
		}

		public static readonly Bound UnKnowBound = new Bound(0, 0, 0, 0);

		public Bound(double minLatitude, double minLongitude, double maxLatitude, double maxLongitude) {
			this.MinLatitude = minLatitude;
			this.MinLongitude = minLongitude;
			this.MaxLatitude = maxLatitude;
			this.MaxLongitude = maxLongitude;
		}

		public void Deconstruct(out double minLatitude, out double minLongitude, out double maxLatitude, out double maxLongitude) {
			maxLongitude = MaxLongitude;
			maxLatitude = MaxLatitude;
			minLongitude = MinLongitude;
			minLatitude = MinLatitude;
		}

		public double MaxLongitude { get; set; }

		public double MaxLatitude { get; set; }

		public double MinLongitude { get; set; }

		public double MinLatitude { get; set; }

		public void UpdateBound(double latitude, double longitude) {
			MaxLongitude = Math.Max(MaxLongitude, longitude);
			MaxLatitude = Math.Max(MaxLatitude, latitude);
			MinLongitude = Math.Min(MinLongitude, longitude);
			MinLatitude = Math.Min(MinLatitude, latitude);
		}

		public override string ToString() {
			return JsonSerializer.Serialize(this);
		}

	}

	public class OsmRouterData {

		private OsmRouterData(Router router, List<Vehicle> vehicles, Bound bounds) {
			Vehicles = new List<Vehicle>(vehicles);
			Bounds = bounds;
			Router = router;
			Success = true;
			Message = String.Empty;
		}
		
		private OsmRouterData(string message) {
			Success = false;
			Message = message;
		}
		
		public static OsmRouterData CreateSuccess(Router router, List<Vehicle> vehicles, Bound bounds) {
			return new OsmRouterData(router, vehicles, bounds);
		}

		public static OsmRouterData CreateFailed(string message) {
			return new OsmRouterData(message);
		}

		public string Message { get; init; }
		
		public bool Success { get; init; }

		private List<Vehicle> Vehicles { get; init; }
		
		public Bound Bounds { get; init; } 
		
		public Router Router { get; init; }
		
	}

	public class OsmLoadUtil {
				
		public readonly static string OsmSuffix = ".osm";
		
		public readonly static string RouterDbSuffix = ".routerdb";
		
		public readonly static string PbfSuffix = ".pbf";
		
		public readonly static string OsmBounds = "bounds";
		
		public static OsmRouterData LoadRouterAndSaveFromOverpass(string savePath, Bound bound, List<Vehicle> vehicles) {
			RouterDb routerDb = new RouterDb();
			Box box = new Box((float)bound.MinLatitude, (float)bound.MinLongitude, (float)bound.MaxLatitude, (float)bound.MaxLongitude);
			try {
				routerDb.LoadOsmDataFromOverpass(box, vehicles.ToArray());
			} catch (Exception) {
				return OsmRouterData.CreateFailed($"在线加载路网失败:{bound}");
			}
			try {
				using (FileStream fileStream = File.OpenWrite(savePath)) {
					routerDb.Meta.Add(new Tag(OsmBounds, bound.ToString()));
					routerDb.Serialize(fileStream);
				}
			} catch (Exception) {
				return OsmRouterData.CreateFailed($"在线加载路网成功，保存失败:{savePath}");
			}
			return OsmRouterData.CreateSuccess(new Router(routerDb), vehicles, bound);
		}

		public static OsmRouterData LoadRouterByOsmFile(string filePath, List<Vehicle> passengerType) {
			if (!File.Exists(filePath)) {
				return OsmRouterData.CreateFailed($"文件不存在：{filePath}");
			}
			if (filePath.EndsWith(RouterDbSuffix)) {
				return OsmDataTransferUtil.DeserializeRouterDb(filePath, passengerType);
			}
			if (!filePath.EndsWith(OsmSuffix)) {
				return OsmRouterData.CreateFailed($"文件格式不存在或格式不正确：{filePath}");
			}
			return OsmDataTransferUtil.TransferOsmToRouter(filePath, passengerType);
		}
		
		private static Bound GetOsmBounds(string filePath) {
			XmlDocument doc = new XmlDocument();
			using XmlTextReader reader = new XmlTextReader(filePath);
			doc.Load(reader);
			XmlNodeList elementList = doc.GetElementsByTagName(OsmBounds);
			if (elementList.Count != 1) {
				return null;
			}
			XmlNode node = elementList.Item(0);
			if (node?.Attributes != null) {
				var minLat = Convert.ToDouble(node.Attributes["minlat"]?.Value);
				var minLon = Convert.ToDouble(node.Attributes["minlon"]?.Value);
				var maxLat = Convert.ToDouble(node.Attributes["maxlat"]?.Value);
				var maxLon = Convert.ToDouble(node.Attributes["maxlon"]?.Value);
				return new Bound(minLat, minLon, maxLat, maxLon);
			}
			return null;
		}

	}

}